

    // ==UserScript==
    // @name         embyLaunchPotplayer
    // @name:en      embyLaunchPotplayer
    // @name:zh      embyLaunchPotplayer
    // @name:zh-CN   embyLaunchPotplayer
    // @namespace    http://tampermonkey.net/
    // @version      1.0.5
    // @description  emby launch extetnal player
    // @description:zh-cn emby调用外部播放器
    // @license      MIT
    // @author       @bpking
    // @include      */web/index.html
    // ==/UserScript==
     
    (function () {
        'use strict';
        setInterval(function () {
            let potplayer = document.querySelectorAll("div[is='emby-scroller']:not(.hide) #embyPot")[0];
            if (!potplayer) {
                let mainDetailButtons = document.querySelectorAll("div[is='emby-scroller']:not(.hide) .mainDetailButtons")[0];
                if (mainDetailButtons) {
                    let buttonhtml = `<div class ="detailButtons  flex align-items-flex-start flex-wrap-wrap">
                      <button id="embyPot" type="button" class="detailButton  emby-button emby-button-backdropfilter raised-backdropfilter detailButton-primary" title="Potplayer"> <div class="detailButton-content"> <i class="md-icon detailButton-icon button-icon button-icon-left"></i>  <span class="button-text">Pot</span> </div> </button>
                      <button id="embyVlc" type="button" class="detailButton  emby-button emby-button-backdropfilter raised-backdropfilter detailButton-primary" title="VLC"> <div class="detailButton-content"> <i class="md-icon detailButton-icon button-icon button-icon-left"></i>  <span class="button-text">VLC</span>  </div> </button>
                      <button id="embyIINA" type="button" class="detailButton  emby-button emby-button-backdropfilter raised-backdropfilter detailButton-primary" title="IINA"> <div class="detailButton-content"> <i class="md-icon detailButton-icon button-icon button-icon-left"></i>  <span class="button-text">IINA</span> </div> </button>
                      <button id="embyNPlayer" type="button" class="detailButton  emby-button emby-button-backdropfilter raised-backdropfilter detailButton-primary" title="NPlayer"> <div class="detailButton-content"> <i class="md-icon detailButton-icon button-icon button-icon-left"></i>  <span class="button-text">NPlayer</span> </div> </button>
                      <button id="embyMX" type="button" class="detailButton  emby-button emby-button-backdropfilter raised-backdropfilter detailButton-primary" title="MXPlayer"> <div class="detailButton-content"> <i class="md-icon detailButton-icon button-icon button-icon-left"></i>  <span class="button-text">MX</span> </div> </button>
                      <button id="embyCopyUrl" type="button" class="detailButton  emby-button emby-button-backdropfilter raised-backdropfilter detailButton-primary" title="复制串流地址"> <div class="detailButton-content"> <i class="md-icon detailButton-icon button-icon button-icon-left"></i>  <span class="button-text">复制链接</span> </div> </button>
                          </div>`
                    mainDetailButtons.insertAdjacentHTML('afterend', buttonhtml)
                    document.querySelector("div[is='emby-scroller']:not(.hide) #embyPot").onclick = embyPot;
                    document.querySelector("div[is='emby-scroller']:not(.hide) #embyIINA").onclick = embyIINA;
                    document.querySelector("div[is='emby-scroller']:not(.hide) #embyNPlayer").onclick = embyNPlayer;
                    document.querySelector("div[is='emby-scroller']:not(.hide) #embyMX").onclick = embyMX;
                    document.querySelector("div[is='emby-scroller']:not(.hide) #embyCopyUrl").onclick = embyCopyUrl;
                    document.querySelector("div[is='emby-scroller']:not(.hide) #embyVlc").onclick = embyVlc;
                }
            }
        }, 1000);
     
     
        async function getItemInfo() {
            let userId = ApiClient._serverInfo.UserId;
            let itemId = /\?id=(\d*)/.exec(window.location.hash)[1];
            let response = await ApiClient.getItem(userId, itemId);
            //继续播放当前剧集的下一集
            if (response.Type == "Series") {
                let seriesNextUpItems = await ApiClient.getNextUpEpisodes({ SeriesId: itemId, UserId: userId });
                console.log("nextUpItemId: " + seriesNextUpItems.Items[0].Id);
                return await ApiClient.getItem(userId, seriesNextUpItems.Items[0].Id);
            }
            //播放当前季season的第一集
            if (response.Type == "Season") {
                let seasonItems = await ApiClient.getItems(userId, { parentId: itemId });
                console.log("seasonItemId: " + seasonItems.Items[0].Id);
                return await ApiClient.getItem(userId, seasonItems.Items[0].Id);
            }
            //播放当前集或电影
            console.log("itemId:  " + itemId);
            return response;
        }
     
        function getSeek(position) {
            let ticks = position * 10000;
            let parts = []
                , hours = ticks / 36e9;
            (hours = Math.floor(hours)) && parts.push(hours);
            let minutes = (ticks -= 36e9 * hours) / 6e8;
            ticks -= 6e8 * (minutes = Math.floor(minutes)),
                minutes < 10 && hours && (minutes = "0" + minutes),
                parts.push(minutes);
            let seconds = ticks / 1e7;
            return (seconds = Math.floor(seconds)) < 10 && (seconds = "0" + seconds),
                parts.push(seconds),
                parts.join(":")
        }
     
        function getSubPath(mediaSource) {
            let selectSubtitles = document.querySelector("div[is='emby-scroller']:not(.hide) select.selectSubtitles");
            let subTitlePath = '';
            //返回选中的外挂字幕
            if (selectSubtitles && selectSubtitles.value > 0) {
                if (mediaSource.MediaStreams[selectSubtitles.value].IsExternal) {
                    let subtitleCodec = mediaSource.MediaStreams[selectSubtitles.value].Codec;
                    subTitlePath = `/${mediaSource.Id}/Subtitles/${selectSubtitles.value}/Stream.${subtitleCodec}`;
                }
            }
            else {
                //默认尝试返回第一个外挂中文字幕
                let chiSubIndex = mediaSource.MediaStreams.findIndex(m => m.Language == "chi" && m.IsExternal);
                if (chiSubIndex > -1) {
                    let subtitleCodec = mediaSource.MediaStreams[chiSubIndex].Codec;
                    subTitlePath = `/${mediaSource.Id}/Subtitles/${chiSubIndex}/Stream.${subtitleCodec}`;
                } else {
                    //尝试返回第一个外挂字幕
                    let externalSubIndex = mediaSource.MediaStreams.findIndex(m => m.IsExternal);
                    if (externalSubIndex > -1) {
                        let subtitleCodec = mediaSource.MediaStreams[externalSubIndex].Codec;
                        subTitlePath = `/${mediaSource.Id}/Subtitles/${externalSubIndex}/Stream.${subtitleCodec}`;
                    }
                }
     
            }
            return subTitlePath;
        }
     
     
        async function getEmbyMediaInfo() {
            let itemInfo = await getItemInfo();
            let mediaSourceId = itemInfo.MediaSources[0].Id;
            let selectSource = document.querySelector("div[is='emby-scroller']:not(.hide) select.selectSource");
            if (selectSource && selectSource.value.length > 0) {
                mediaSourceId = selectSource.value;
            }
            //let selectAudio = document.querySelector("div[is='emby-scroller']:not(.hide) select.selectAudio");
            let mediaSource = itemInfo.MediaSources.find(m => m.Id == mediaSourceId);
            let domain = `${ApiClient._serverAddress}/emby/videos/${itemInfo.Id}`;
            let subPath = getSubPath(mediaSource);
            let subUrl = subPath.length > 0 ? `${domain}${subPath}?api_key=${ApiClient.accessToken()}` : '';
            let streamUrl = `${domain}/stream.${mediaSource.Container}?api_key=${ApiClient.accessToken()}&Static=true&MediaSourceId=${mediaSourceId}`;
            let position = parseInt(itemInfo.UserData.PlaybackPositionTicks / 10000);
            let intent = await getIntent(mediaSource, position);
            console.log(streamUrl, subUrl, intent);
            return {
                streamUrl: streamUrl,
                subUrl: subUrl,
                intent: intent,
            }
        }
     
        async function getIntent(mediaSource, position) {
            let title = mediaSource.Path.split('/').pop();
            let externalSubs = mediaSource.MediaStreams.filter(m => m.IsExternal == true);
            let subs = ''; //要求是android.net.uri[] ?
            let subs_name = '';
            let subs_filename = '';
            let subs_enable = '';
            if (externalSubs) {
                subs_name = externalSubs.map(s => s.DisplayTitle);
                subs_filename = externalSubs.map(s => s.Path.split('/').pop());
            }
            return {
                title: title,
                position: position,
                subs: subs,
                subs_name: subs_name,
                subs_filename: subs_filename,
                subs_enable: subs_enable
            };
        }
     
        async function embyPot() {
            let mediaInfo = await getEmbyMediaInfo();
            let intent = mediaInfo.intent;
            let poturl = `potplayer://${encodeURI(mediaInfo.streamUrl)} /sub="${encodeURI(mediaInfo.subUrl)}" /current /title="${encodeURI(intent.title)}" /seek=${getSeek(intent.position)}`;
            console.log(poturl);
            window.open(poturl, "_blank");
        }
     
        //https://wiki.videolan.org/Android_Player_Intents/
        async function embyVlc() {
            let mediaInfo = await getEmbyMediaInfo();
            let intent = mediaInfo.intent;
            //android subtitles:  https://code.videolan.org/videolan/vlc-android/-/issues/1903
            let vlcUrl = `intent:${encodeURI(mediaInfo.streamUrl)}#Intent;package=org.videolan.vlc;type=video/*;S.subtitles_location=${encodeURI(mediaInfo.subUrl)};S.title=${encodeURI(intent.title)};i.position=${intent.position};end`;
            if (getOS() == "windows") {
                //桌面端需要额外设置,参考这个项目,MPV也是类似的方法:  https://github.com/stefansundin/vlc-protocol 
                vlcUrl = `vlc://${encodeURI(mediaInfo.streamUrl)}`;
            }
            if (getOS() == 'ios') {
                //https://code.videolan.org/videolan/vlc-ios/-/commit/55e27ed69e2fce7d87c47c9342f8889fda356aa9
                vlcUrl = `vlc-x-callback://x-callback-url/stream?url=${encodeURI(mediaInfo.streamUrl)}&sub=${encodeURI(mediaInfo.subUrl)}`;
            }
            console.log(vlcUrl);
            window.open(vlcUrl, "_blank");
        }
     
        //https://github.com/iina/iina/issues/1991
        async function embyIINA() {
            let mediaInfo = await getEmbyMediaInfo();
            let iinaUrl = `iina://weblink?url=${encodeURI(mediaInfo.streamUrl)}&new_window=1`;
            console.log(`iinaUrl= ${iinaUrl}`);
            window.open(iinaUrl, "_blank");
        }
     
        //https://sites.google.com/site/mxvpen/api
        async function embyMX() {
            let mediaInfo = await getEmbyMediaInfo();
            let intent = mediaInfo.intent;
            //mxPlayer free
            let mxUrl = `intent:${encodeURI(mediaInfo.streamUrl)}#Intent;package=com.mxtech.videoplayer.ad;S.title=${encodeURI(intent.title)};i.position=${intent.position};end`;
            //mxPlayer Pro
            //let mxUrl = `intent:${encodeURI(mediaInfo.streamUrl)}#Intent;package=com.mxtech.videoplayer.pro;S.title=${encodeURI(intent.title)};i.position=${intent.position};end`;
            console.log(mxUrl);
            window.open(mxUrl, "_blank");
        }
     
        async function embyNPlayer() {
            let mediaInfo = await getEmbyMediaInfo();
            let nUrl = `nplayer-${encodeURI(mediaInfo.streamUrl)}`;
            console.log(nUrl);
            window.open(nUrl, "_blank");
        }
     
        async function embyCopyUrl() {
            let mediaInfo = await getEmbyMediaInfo();
            let textarea = document.createElement('textarea');
            document.body.appendChild(textarea);
            textarea.style.position = 'absolute';
            textarea.style.clip = 'rect(0 0 0 0)';
            textarea.value = mediaInfo.streamUrl;
            textarea.select();
            if (document.execCommand('copy', true)) {
                console.log(`copyUrl = ${mediaInfo.streamUrl}`);
                this.innerText = '复制成功';
            }
            //need https
            // if (navigator.clipboard) {
            //     navigator.clipboard.writeText(mediaInfo.streamUrl).then(() => {
            //          console.log(`copyUrl = ${mediaInfo.streamUrl}`);
            //          this.innerText = '复制成功';
            //     })
            // }
        }
     
        function getOS() {
            let u = navigator.userAgent
            if (!!u.match(/compatible/i) || u.match(/Windows/i)) {
                return 'windows'
            } else if (!!u.match(/Macintosh/i) || u.match(/MacIntel/i)) {
                return 'macOS'
            } else if (!!u.match(/iphone/i) || u.match(/Ipad/i)) {
                return 'ios'
            } else if (u.match(/android/i)) {
                return 'android'
            } else if (u.match(/Ubuntu/i)) {
                return 'Ubuntu'
            } else {
                return 'other'
            }
        }
     
    })();
     

